Skip to main content

Dealing with runtime constants

Constants in Substrate runtimes serve as fixed, unchangeable values that provide configuration data to the various components and pallets. These constants are set at compile-time and typically use the parameter_types! macro for their declaration. This allows them to benefit from type checking and are embedded in the metadata, making them easily accessible and inspectable from external tools or interfaces.

In this Substrate-in-Bits tutorial, we’ll take a problem-based approach to explore why runtime constants are declared within the parameter_types! macro, and why declaring an external constant can lead to issues/complications such as exclusion from the chain's metadata, manual implementation of the Get trait, and increased risk of runtime errors.

To help us measure our progress and improve Substrate in Bits content, please fill out our living feedback form. It will only take 2 minutes of your time. Thank you!

Reproducing errors

Environment and project setup

To follow along with this tutorial, ensure that you have the Rust toolchain installed.

git clone -b dealing-with-runtime-constants --single-branch https://github.com/abdbee/sibchain.git
  • navigate to the project’s directory
cd sibchain

  • Run the command below to compile the node
cargo build --release

While attempting to compile the node above, you’ll encounter an error similar to the one below:

error[E0573]: expected type, found constant `MaxAmount`
--> /Users/abdulbee/Desktop/OpenSource/sibchain/runtime/src/lib.rs:278:19
|
278 | type MaxAmount = MaxAmount;
| ^^^^^^^^^ help: you might have meant to use the associated type: `Self::MaxAmount`

error[E0152]: duplicate lang item in crate `std`: `panic_impl`.
|
= note: the lang item is first defined in crate `sp_io` (which `sp_application_crypto` depends on)
= note: first definition in `sp_io` loaded from /Users/abdulbee/Desktop/OpenSource/sibchain/target/release/wbuild/node-template-runtime/target/wasm32-unknown-unknown/release/deps/libsp_io-1fb5e909d9013cdb.rmeta
= note: second definition in `std` loaded from /Users/abdulbee/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/wasm32-unknown-unknown/lib/libstd-434f4a63fb8fd380.rlib

Some errors have detailed explanations: E0152, E0573.
For more information about an error, try `rustc --explain E0152`.
error: could not compile `node-template-runtime` (lib) due to 2 previous errors

This error is occurring because the compiler expected the types used for the MaxAmount constant when configuring pallets in the runtime should implement the Get trait.

#[pallet::config]
pub trait Config: frame_system::Config {
/// --- snip ---
type MaxAmount: Get<u64>;
}

But instead, a bare constant was found. A quick fix would have been to create a MaxAmount type and manually implement the Get trait for this type:

// Create a struct to act as a wrapper
struct MaxAmount;

// Implement the Get trait for the wrapper
impl Get<u32> for MaxAmount {
fn get() -> u32 {
MAX_AMOUNT
}
}

However, this is where the parameter_types! macro comes in. This macro not only automatically implements the Get trait for your constants but also ensures that these constants are properly integrated into the chain's metadata. This makes them easily accessible and modifiable through on-chain governance. In-depth information about the parameter_types! macro can be found in the going in-depth section.

Solving the error

To solve the error,

  • Navigate to the runtime’s lib.rs file
cd runtime/src/lib.rs
  • Create the parameter_types! macro and move const MaxAmount: u64 = 1_000_000; inside the macro
parameter_types! {
pub const MaxAmount: u64 = 1_000_000;
}
  • Compile the node
cargo build --release

Going in-depth

The parameter_types! macro is a Rust procedural macro in Substrate that allows you to define constants for your runtime logic. At its core, this macro automatically implements the Get trait for your constants. This allows these constant values to be fetched efficiently across the entire runtime, wherever the Get trait is implemented or expected.

parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
}

In standard Rust, constants are usually defined with the const keyword. But in Substrate, using parameter_types! provides type-safety benefits. It makes sure that the constants are compatible with the types expected in the pallet configuration traits.

One of the most compelling features of the parameter_types! macro is its seamless integration with the chain's metadata. This makes these constants not only easily accessible but also easily modifiable. That is, the values of these constants can be updated through a decentralized voting mechanism, avoiding the need for manual code changes.

Why is type safety so crucial here? In most languages, the type of a constant is determined solely by its initial value. However, Substrate often requires your constants to interact with generic types in pallet traits. The parameter_types! macro ensures that your constants fulfill these trait bounds, thus providing type safety.

#[pallet::config]
pub trait Config: frame_system::Config {
type MaxAmount: Get<u64>;
}

In this case, using parameter_types! ensures that the constant will meet the required trait bound of Get<u32>.

When a constant is used in multiple locations across a pallet, the Get trait fetches it as a reference, saving memory and computational power. This performance optimization, though subtle, can add up in a blockchain runtime where efficiency

Errors in Substrate are not just mere annoyances; they can sometimes be enigmatic. One such common error occurs when constants are not defined within the parameter_types! macro.

Here's a simplified example of a typical error message:

error[E0573]: expected type, found constant `MaxAmount`

What this is telling you is that the Rust compiler expected a type that implements the Get trait, but found a raw constant instead. Without using parameter_types!, the constant hasn't been wrapped with a type that implements the Get trait, leading to compatibility issues.

This leads to a more serious error:

error[E0152]: duplicate lang item in crate `std`: `panic_impl`.

This error indicates a more systemic issue related to linking multiple standard libraries, which can happen due to incorrect constant configuration.

Summary

The article serves as a hands-on tutorial for dealing with constants in Substrate runtimes, highlighting the advantages of using the parameter_types! macro.

This macro streamlines the process of declaring constants by automatically implementing the Get trait, providing type safety, and allowing for easy accessibility and modification. The tutorial illustrates this by showing how defining pallet constants outside the parameter_types! macro can lead to compile-time errors and even more complex issues related to system libraries.

To learn more about the concepts discussed in this guide, here are some resources that we recommend:

To help us measure our progress and improve Substrate in Bits content, please fill out our living feedback form. It will only take 2 minutes of your time. Thank you!

grillchat icon